package com.jackstraw.jack_base.rules.config;


import com.jackstraw.jack_base.rules.entity.bo.RuleGroupBo;
import com.jackstraw.jack_base.rules.entity.bo.SceneActuatorBo;
import com.jackstraw.jack_base.rules.entity.enums.ResultEnum;
import com.jackstraw.jack_base.rules.exception.BadRuleException;
import com.jackstraw.jack_base.rules.service.RuleExcuteService;
import com.jackstraw.jack_base.util.loader.classloader.DynamicClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.io.Resource;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Drools工具类
 */
@Slf4j
public class DroolsUtil {


    /**
     * KieBase 缓存(key：规则组ID)
     */
    private final Map<Long, KieBase> ruleGroupMap = new ConcurrentHashMap<>();

    /**
     * KieBase版本 缓存(key：规则组ID)
     */
    private final Map<Long, Integer> ruleVersionMap = new ConcurrentHashMap<>();


    /**
     * 场景执行器 缓存(key：场景ID)
     */
    private static final Map<String, RuleExcuteService> sceneExcuteServiceMap
            = new ConcurrentHashMap<>();

    /**
     * 场景执行器版本 缓存(key：场景ID)
     */
    private static final Map<String, Integer> sceneExcuteVersionMap
            = new ConcurrentHashMap<>();


    private DroolsUtil() {
    }

    private static class SingletonHolder {
        static DroolsUtil instance = new DroolsUtil();
    }

    public static DroolsUtil getInstance() {
        return SingletonHolder.instance;
    }


    public RuleExcuteService ruleExcuteService(final SceneActuatorBo sceneActuatorBo) {
        try {
            DynamicClass compiler = DynamicClass.init(sceneActuatorBo.getSceneActuatorCode(), sceneActuatorBo.getFullClassName()).compiler();
            RuleExcuteService ruleExcuteService = (RuleExcuteService) compiler.load().newInstance();
            ruleExcuteService.setRuleGroupIdS(sceneActuatorBo.getRuleGroupIdS());
            sceneExcuteServiceMap.put(sceneActuatorBo.getSceneName(), ruleExcuteService);
            sceneExcuteVersionMap.put(sceneActuatorBo.getSceneName(), sceneActuatorBo.getVersion());
            return ruleExcuteService;
        } catch (Exception e) {
            log.error("生成场景执行器[{}]异常，异常原因为：", sceneActuatorBo.getSceneName(), e);
            throw new BadRuleException(ResultEnum.SYSTEM_ERROR, e);
        }
    }


    public KieBase kieBase(final RuleGroupBo ruleGroupBO) {
        return this.kieBase(ruleGroupBO.getRuleGroupId(), ruleGroupBO.getPackageName(),
                ruleGroupBO.getVersion(), ruleGroupBO.getRuleDrl());
    }

//    /**
//     * 方法说明: 加载drl文件，并将编译后的KieBase存入缓存
//     * 还可以校验规则文件是否正确
//     * 此方法会造成KieBase无法共用问题
//     * @param groupID     规则组ID
//     * @param packageName 包名
//     * @param version     版本
//     * @param rule        规则字符串
//     */
//    public KieBase kieBase(final String groupID, final String packageName, final Integer version, final String rule) {
//        // 设置时间格式
//        System.setProperty("drools.dateformat", "yyyy-MM-dd");
//        //为防止规则文件名字重复，此处加上雪花ID( 格式：规则组ID+雪花ID+.drl)
//        String ruleFileName = groupID + RulesSequence.next(SequenceEnum.UNIQUE_BUSINESS_NO) + ".drl";
//        KieServices kieServices = KieServices.Factory.get();
//        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
//        String path = "src/main/resources/rules/" + packageName.replaceAll("\\.", "/") + "/" + ruleFileName;
//        kieFileSystem.write(path, rule);
//        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem).buildAll();
//        if (kieBuilder.getResults().getMessages(Message.Level.ERROR).size() > 0) {
//            logger.error("规则文件编译错误，请查看错误信息：{}", kieBuilder.getResults().getMessages().toString());
//            throw new RulesException(ResultEnum.RULE_ERROR);
//        }
//        KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
//        KieBase kieBase = kieContainer.getKieBase();
//        this.ruleGroupMap.put(groupID, kieBase);
//        this.ruleVersionMap.put(groupID, version);
//        return kieBase;
//    }

    /**
     * 方法说明: 加载drl文件，并将编译后的KieBase存入缓存
     * 还可以校验规则文件是否正确
     *
     * @param ruleGroupId 规则组ID
     * @param packageName 包名
     * @param version     版本
     * @param ruleDrl     规则字符串
     */
    public KieBase kieBase(final Long ruleGroupId, final String packageName, final Integer version, final String ruleDrl) {
        // 设置时间格式
        System.setProperty("drools.dateformat", "yyyy-MM-dd");
        //为防止规则文件名字重复，此处加上雪花ID( 格式：规则组ID+雪花ID+.drl)
        KieServices kieServices = KieServices.Factory.get();

        Resource resource = kieServices.getResources()
                .newByteArrayResource(ruleDrl.getBytes());

        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(resource, ResourceType.DRL);
        if (kbuilder.hasErrors()) {
            log.error("规则文件编译错误，请查看错误信息：{}", kbuilder.getErrors().toString());
            throw new BadRuleException(ResultEnum.RULE_ERROR);
        }
        KieBase kieBase = kbuilder.newKieBase();

        this.ruleGroupMap.put(ruleGroupId, kieBase);
        this.ruleVersionMap.put(ruleGroupId, version);
        return kieBase;
    }


    /**
     * 通过场景ID获取缓存中场景版本
     *
     * @param sceneId 场景ID
     * @return
     */
    public Integer getSceneVersionInCache(final String sceneId) {
        return sceneExcuteVersionMap.get(sceneId);
    }

    /**
     * 通过场景ID获取缓存中场景执行器
     *
     * @param sceneId 场景ID
     * @return
     */
    public RuleExcuteService getSceneExcuteServiceInCache(final String sceneId) {
        return sceneExcuteServiceMap.get(sceneId);
    }


    /**
     * 通过规则组ID获取缓存中规则组版本
     *
     * @param ruleGroupId 规则组ID
     * @return
     */
    public Integer getVersionInCache(final Long ruleGroupId) {
        return ruleVersionMap.get(ruleGroupId);
    }

    /**
     * 通过规则组ID获取缓存中KieBase
     *
     * @param ruleGroupId 规则组ID
     * @return
     */
    public KieBase getKieBaseInCache(final Long ruleGroupId) {
        return ruleGroupMap.get(ruleGroupId);
    }

    /**
     * 通过ruleGroupId获取缓存中的KieBase,通过创建KieSession，如果返回null，则表示缓存中没有
     *
     * @param ruleGroupId 规则组ID
     */
    public KieSession getKieSessionInCache(final Long ruleGroupId) {
        KieBase kbase = this.getKieBaseInCache(ruleGroupId);
        if (kbase == null) {
            return null;
        } else {
            return kbase.newKieSession();
        }
    }

    /**
     * 移除对应的规则
     *
     * @param ruleGroupId 规则组ID
     */
    public void clearRuleGroupMap(final Long ruleGroupId) {
        ruleGroupMap.remove(ruleGroupId);
        ruleVersionMap.remove(ruleGroupId);
    }

    /**
     * 清空规则缓存
     */
    public void clearRuleGroupMapAll() {
        ruleGroupMap.clear();
        ruleVersionMap.clear();
    }

    /**
     * 移除对应的场景
     * 同时情况对应的规则
     *
     * @param sceneName 场景名称
     */
    public void clearSceneMap(final String sceneName) {
        sceneExcuteVersionMap.remove(sceneName);
        RuleExcuteService ruleExcuteService = sceneExcuteServiceMap.get(sceneName);
        if (ObjectUtils.isNotEmpty(ruleExcuteService)) {
            ruleExcuteService.getRuleGroupIdS()
                    .stream()
                    .forEach(groupID
                            -> this.clearRuleGroupMap(groupID));
            ruleExcuteService.getRuleGroupIdS().clear();
        }
        sceneExcuteServiceMap.remove(sceneName);
    }

    /**
     * 清空场景缓存
     * 同时清空所有的规则缓存
     */
    public void clearSceneMapAll() {
        sceneExcuteVersionMap.clear();
        this.clearRuleGroupMapAll();
        sceneExcuteServiceMap.clear();
    }


}
